/*
 * address_decode.c
 *
 *  Created on: Jan 21, 2014
 *      Author: Raviraj
 */

#include"includes.h"

/* An operand specifier consists of an addressing mode and a register.
   The addressing modes are:

   0    register direct         R               op = R
   1    register deferred       (R)             op = M[R]
   2    autoincrement           (R)+            op = M[R]; R = R + length
   3    autoincrement deferred  @(R)+           op = M[M[R]]; R = R + 2
   4    autodecrement           -(R)            R = R - length; op = M[R]
   5    autodecrement deferred  @-(R)           R = R - 2; op = M[M[R]]
   6    displacement            d(R)            op = M[R + disp]
   7    displacement deferred   @d(R)           op = M[M[R + disp]]

   There are eight general registers, R0-R7.  R6 is the stack pointer,
   R7 the PC.  The combination of addressing modes with R7 yields:

   27   immediate               #n              op = M[PC]; PC = PC + 2
   37   absolute                @#n             op = M[M[PC]]; PC = PC + 2
   67   relative                d(PC)           op = M[PC + disp]
   */


unsigned short get_operand_address(unsigned short mode, unsigned short operand_reg)
{
	unsigned short ret_address = 0;
	switch(mode)
	{
	case GeneralRegAM:
		ret_address = add_mode0 (operand_reg);
		break;

	case DeferredRegAM:
		ret_address = add_mode1 (operand_reg);
		break;

	case AutoIncAM:
		ret_address = add_mode2 (operand_reg);
		break;

	case DeferredAutoIncAM:
		ret_address = add_mode3 (operand_reg);
		break;

	case AutoDecAM:
		ret_address = add_mode4 (operand_reg);
		break;

	case DeferredAutoDecAM:
		ret_address = add_mode5 (operand_reg);
		break;

	case IndexedAM:
		ret_address = add_mode6 (operand_reg);
		break;

	case DeferredIndexedAM:
		ret_address = add_mode7 (operand_reg);
		break;

	default :
		printf("\n  Error :  Invalid addressing mode.");
		exit_on_error();
		//printf("\n");
	}
	return ret_address;
}


unsigned short add_mode0 (unsigned short operand_reg)
{
	return operand_reg;
}

unsigned short add_mode1 (unsigned short operand_reg)
{
	return read_from_memory(operand_reg,DATA);
}

unsigned short add_mode2 (unsigned short operand_reg)
{
	unsigned short ret_address = 0;

	ret_address = read_from_memory(operand_reg,DATA);
	write_in_memory(operand_reg,(ret_address+2),DATA);
	return ret_address;
}

unsigned short add_mode3 (unsigned short operand_reg)
{
	unsigned short ret_address = 0;

	ret_address = read_from_memory(operand_reg,DATA);
    write_in_memory(operand_reg,(ret_address+2),DATA);
    ret_address = read_from_memory(ret_address,DATA);
    return ret_address;
}

unsigned short add_mode4 (unsigned short operand_reg)
{
	unsigned short ret_address = 0;

	ret_address = read_from_memory(operand_reg,DATA);
    write_in_memory(operand_reg,(ret_address-2),DATA);
    return (ret_address-2);
}

unsigned short add_mode5 (unsigned short operand_reg)
{
	unsigned short ret_address = 0;

	ret_address = read_from_memory(operand_reg,DATA);
	write_in_memory(operand_reg,(ret_address - 2),DATA);
	ret_address = read_from_memory(ret_address - 2,DATA);
	return ret_address;
}

unsigned short add_mode6 (unsigned short operand_reg)
{
	unsigned short ret_address = 0;
	unsigned short offset;
	unsigned short PC_val;

	PC_val = get_Program_Counter();
	offset = read_from_memory(PC_val,DATA);
	set_Program_Counter(PC_val + 2);
	ret_address = read_from_memory(operand_reg,DATA);
	ret_address = ret_address + offset;
	return ret_address;
}

unsigned short add_mode7 (unsigned short operand_reg)
{
	unsigned short ret_address = 0;
	unsigned short offset;
	unsigned short PC_val;

	PC_val = get_Program_Counter();
	offset = read_from_memory(PC_val,DATA);
	set_Program_Counter(PC_val + 2);
	ret_address = read_from_memory(operand_reg,DATA);
	ret_address = ret_address + offset;
	ret_address = read_from_memory(ret_address,DATA);
	return ret_address;
}
